package org.dmikis.task11.gui;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

import javax.swing.*;

import org.dmikis.task11.PhasePoint;
import org.dmikis.task11.engine.Engine;


public class GraphPanel extends JPanel {
    
    public GraphPanel() {
	super();
	this.addMouseListener(new MouseListener() {

	    @Override
	    public void mouseClicked(MouseEvent e) {
		if (e.getClickCount() == 2) {
		    parametersDialog.showDialog();
		    if (parametersDialog.isOK) {
			engine.setErrorValue(parametersDialog.getNewError());
			engine.setStartingPoint(parametersDialog.getNewStartingPoint());
			engine.run();
			repaint();
		    }
		}
	    }

	    @Override
	    public void mouseEntered(MouseEvent e) {
		// Unsupported now
	    }

	    @Override
	    public void mouseExited(MouseEvent e) {
		// Unsupported now
	    }

	    @Override
	    public void mousePressed(MouseEvent e) {
		// Unsupported now
	    }

	    @Override
	    public void mouseReleased(MouseEvent e) {
		// Unsupported now
	    }
	});
    }
    
    public void setEngine(Engine engine) {
	this.engine = engine;
	this.parametersDialog.setParametersList(engine.getEngineParameters());
    }
        
    public void paintComponent(Graphics g) {
	g.clearRect(0, 0, this.getSize().width, this.getSize().height);
	System.err.println(this.engine.getTimeStep());
	Graphics2D g2 = (Graphics2D) g;
	g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
	g2.draw(new Rectangle2D.Double(
		LEFT_M, TOP_M,
		this.getSize().getWidth() / 3 - LEFT_M - RIGHT_M, this.getSize().getHeight() - TOP_M - BOTTOM_M));
	double[] timeline = engine.getTimeline();
	double t_scale = (this.getSize().getWidth() / 3 - LEFT_M - RIGHT_M) / (timeline[timeline.length-1] - timeline[0]);
	double min_v = Double.MAX_VALUE, max_v = Double.MIN_VALUE;
	double min_tetta = Double.MAX_VALUE, max_tetta = Double.MIN_VALUE;
	for (int i=0; i<timeline.length; i++) {
	    PhasePoint p = engine.getPoint(timeline[i]);
	    min_v = (p.getV() < min_v) ? p.getV() : min_v;
	    max_v = (p.getV() > max_v) ? p.getV() : max_v;
	    min_tetta = (p.getTetta() < min_tetta) ? p.getTetta() : min_tetta;
	    max_tetta = (p.getTetta() > max_tetta) ? p.getTetta() : max_tetta;
	}
	double tetta_scale = (this.getSize().getHeight() - TOP_M - BOTTOM_M) / (max_tetta - min_tetta);
	double v_scale = (this.getSize().getHeight() - TOP_M - BOTTOM_M) / (max_v - min_v);
	for (int i=0; i<=TICKS_NUM; i++) {
	    String tick_label;
	    g2.drawLine(
		    (int) (LEFT_M + i*t_scale*(timeline[timeline.length - 1] - timeline[0]) / TICKS_NUM),
		    (int) (this.getSize().getHeight() - BOTTOM_M),
		    (int) (LEFT_M + i*t_scale*(timeline[timeline.length - 1] - timeline[0]) / TICKS_NUM),
		    (int) (this.getSize().getHeight() - BOTTOM_M - TICK_LEN));
	    tick_label = String.format("%.2f", new Double(i * (timeline[timeline.length - 1] - timeline[0]) / TICKS_NUM)); 
	    g2.drawString(
		    tick_label,
		    (int) (LEFT_M + i*t_scale*(timeline[timeline.length - 1] - timeline[0]) / TICKS_NUM - 0.5*g2.getFontMetrics().stringWidth(tick_label)),
		    (int) (this.getSize().getHeight() - BOTTOM_M + g2.getFontMetrics().getHeight()));
	    g2.drawLine(
		    (int) (LEFT_M),
		    (int) (this.getSize().getHeight() - BOTTOM_M - i*v_scale*(max_v - min_v) / TICKS_NUM),
		    (int) (LEFT_M + TICK_LEN),
		    (int) (this.getSize().getHeight() - BOTTOM_M - i*v_scale*(max_v - min_v) / TICKS_NUM));
	    tick_label = String.format("%.2f", new Double(i * (max_v - min_v) / TICKS_NUM  + min_v));
	    g2.drawString(
		    tick_label,
		    (int) (LEFT_M - g2.getFontMetrics().stringWidth(tick_label) - 3.0),
		    (int) (this.getSize().getHeight() - BOTTOM_M - i*v_scale*(max_v - min_v) / TICKS_NUM + 0.5*g2.getFontMetrics().getHeight()));
	}
	Path2D v_plot = new Path2D.Double();
	v_plot.moveTo(LEFT_M, this.getSize().getHeight() - BOTTOM_M - v_scale*(engine.getPoint(timeline[0]).getV()-min_v));
	for (int i=1; i<timeline.length; i++) {
	    v_plot.lineTo(
		    LEFT_M + t_scale*(timeline[i]-timeline[0]),
		    this.getSize().getHeight() - BOTTOM_M - v_scale*(engine.getPoint(timeline[i]).getV() - min_v));
	}
	g2.setColor(Color.red);
	g2.draw(v_plot);
	// -----
	g2.setColor(Color.black);
	g2.draw(new Rectangle2D.Double(
		LEFT_M + this.getSize().getWidth() / 3, TOP_M,
		this.getSize().getWidth() / 3 - LEFT_M - RIGHT_M, this.getSize().getHeight() - TOP_M - BOTTOM_M));
	v_scale = (this.getSize().getWidth() / 3 - LEFT_M - RIGHT_M) / (max_v - min_v);
	for (int i=0; i<=TICKS_NUM; i++) {
	    String tick_label;
	    g2.drawLine(
		    (int) (LEFT_M + i*t_scale*(timeline[timeline.length - 1] - timeline[0]) / TICKS_NUM + this.getSize().getWidth() / 3),
		    (int) (this.getSize().getHeight() - BOTTOM_M),
		    (int) (LEFT_M + i*t_scale*(timeline[timeline.length - 1] - timeline[0]) / TICKS_NUM + this.getSize().getWidth() / 3),
		    (int) (this.getSize().getHeight() - BOTTOM_M - TICK_LEN));
	    tick_label = String.format("%.2f", new Double(i * (timeline[timeline.length - 1] - timeline[0]) / TICKS_NUM)); 
	    g2.drawString(
		    tick_label,
		    (int) (LEFT_M + i*t_scale*(timeline[timeline.length - 1] - timeline[0]) / TICKS_NUM - 0.5*g2.getFontMetrics().stringWidth(tick_label) + this.getSize().getWidth() / 3),
		    (int) (this.getSize().getHeight() - BOTTOM_M + g2.getFontMetrics().getHeight()));
	    g2.drawLine(
		    (int) (this.getSize().getWidth() / 3 + RIGHT_M),
		    (int) (this.getSize().getHeight() - BOTTOM_M - i*tetta_scale*(max_tetta - min_tetta) / TICKS_NUM),
		    (int) (this.getSize().getWidth() / 3 + RIGHT_M + TICK_LEN),
		    (int) (this.getSize().getHeight() - BOTTOM_M - i*tetta_scale*(max_tetta - min_tetta) / TICKS_NUM));
	    tick_label = String.format("%.2f", new Double(i * (max_tetta - min_tetta) / TICKS_NUM  + min_tetta));
	    g2.drawString(
		    tick_label,
		    (int) (this.getSize().getWidth() / 3 + LEFT_M - g2.getFontMetrics().stringWidth(tick_label) - 3.0),
		    (int) (this.getSize().getHeight() - BOTTOM_M - i*tetta_scale*(max_tetta - min_tetta) / TICKS_NUM + 0.5*g2.getFontMetrics().getHeight()));
	}
	Path2D tetta_plot = new Path2D.Double();
	tetta_plot.moveTo(LEFT_M + this.getSize().getWidth() / 3, this.getSize().getHeight() - BOTTOM_M - tetta_scale*(engine.getPoint(timeline[0]).getTetta()-min_tetta));
	for (int i=1; i<timeline.length; i++) {
	    tetta_plot.lineTo(
		    LEFT_M + t_scale*(timeline[i]-timeline[0]) + this.getSize().getWidth() / 3,
		    this.getSize().getHeight() - BOTTOM_M - tetta_scale*(engine.getPoint(timeline[i]).getTetta() - min_tetta));
	}
	g2.setColor(Color.blue);
	g2.draw(tetta_plot);
	// -----
	g2.setColor(Color.black);
	g2.draw(new Rectangle2D.Double(
		LEFT_M + 2 * this.getSize().getWidth() / 3, TOP_M,
		this.getSize().getWidth() / 3 - LEFT_M - RIGHT_M, this.getSize().getHeight() - TOP_M - BOTTOM_M));
	for (int i=0; i<=TICKS_NUM; i++) {
	    String tick_label;
	    g2.drawLine(
		    (int) (LEFT_M + 2 * this.getSize().getWidth() / 3 + i*v_scale*(max_v - min_v) / TICKS_NUM),
		    (int) (this.getSize().getHeight() - BOTTOM_M),
		    (int) (LEFT_M + 2 * this.getSize().getWidth() / 3 + i*v_scale*(max_v - min_v) / TICKS_NUM),
		    (int) (this.getSize().getHeight() - BOTTOM_M - TICK_LEN));
	    tick_label = String.format("%.2f", new Double(i*v_scale*(max_v - min_v) / TICKS_NUM + min_v)); 
	    g2.drawString(
		    tick_label,
		    (int) (LEFT_M + 2 * this.getSize().getWidth() / 3 + i*v_scale*(max_v - min_v) / TICKS_NUM - 0.5*g2.getFontMetrics().stringWidth(tick_label)),
		    (int) (this.getSize().getHeight() - BOTTOM_M + g2.getFontMetrics().getHeight()));
	    g2.drawLine(
		    (int) (LEFT_M + 2 * this.getSize().getWidth() / 3),
		    (int) (this.getSize().getHeight() - BOTTOM_M - i*tetta_scale*(max_tetta - min_tetta) / TICKS_NUM),
		    (int) (LEFT_M + TICK_LEN + 2 * this.getSize().getWidth() / 3),
		    (int) (this.getSize().getHeight() - BOTTOM_M - i*tetta_scale*(max_tetta - min_tetta) / TICKS_NUM));
	    tick_label = String.format("%.2f", new Double(i * (max_tetta - min_tetta) / TICKS_NUM + min_tetta));
	    g2.drawString(
		    tick_label,
		    (int) (LEFT_M - g2.getFontMetrics().stringWidth(tick_label) - 3.0 + 2 * this.getSize().getWidth() / 3),
		    (int) (this.getSize().getHeight() - BOTTOM_M - i*tetta_scale*(max_tetta - min_tetta) / TICKS_NUM + 0.5*g2.getFontMetrics().getHeight()));
	}
	Path2D phase_plot = new Path2D.Double();
	phase_plot.moveTo(
		LEFT_M + 2 * this.getSize().getWidth() / 3 + v_scale*(engine.getPoint(timeline[0]).getV()-min_v),
		this.getSize().getHeight() - BOTTOM_M - tetta_scale*(engine.getPoint(timeline[0]).getTetta()-min_tetta));
	for (int i=1; i<timeline.length; i++) {
	    phase_plot.lineTo(
		    LEFT_M + v_scale*(engine.getPoint(timeline[i]).getV()-min_v) + 2 * this.getSize().getWidth() / 3,
		    this.getSize().getHeight() - BOTTOM_M - tetta_scale*(engine.getPoint(timeline[i]).getTetta() - min_tetta));
	}
	g2.setColor(Color.blue);
	g2.draw(phase_plot);
	g2.drawString(
		"v(t)",
		(int) (LEFT_M - 0.5 * g2.getFontMetrics().stringWidth("v(t)") + 0.5 * (this.getSize().getWidth() / 3 - LEFT_M - RIGHT_M)),
		(int) (this.getSize().getHeight() - 3.0));
	g2.drawString(
		"tetta(t)",
		(int) (LEFT_M - 0.5 * g2.getFontMetrics().stringWidth("v(t)") + 0.5 * (this.getSize().getWidth() / 3 - LEFT_M - RIGHT_M) + this.getSize().getWidth() / 3),
		(int) (this.getSize().getHeight() - 3.0));
	g2.drawString(
		"tetta(v)",
		(int) (LEFT_M - 0.5 * g2.getFontMetrics().stringWidth("v(t)") + 0.5 * (this.getSize().getWidth() / 3 - LEFT_M - RIGHT_M) + 2 * this.getSize().getWidth() / 3),
		(int) (this.getSize().getHeight() - 3.0));
    }
    
    protected Engine engine;
    protected ParametersEditorDialog parametersDialog = new ParametersEditorDialog();
    
    private static final double LEFT_M    = 40.0;
    private static final double RIGHT_M   = 40.0;
    private static final double TOP_M     = 15.0;
    private static final double BOTTOM_M  = 40.0;
    
    private static final double TICK_LEN  = 10.0;
    private static final int    TICKS_NUM = 4;
    
    private static final long serialVersionUID = 1L;
}
